﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace EasyIot.WinApp
{
    public class TcpClientMgr
    {
        static Socket m_socket;
        static string m_ServerIP = "127.0.0.1";
        static int m_ServerPort = 0;

        static bool m_bConnection;
        static bool m_bExit;
        static bool m_bFirst = true;
        static bool m_bHasStop = false;
        public static long lastRecvTime;
        public static int outTime = 2 * 60 * 1000;// 3分钟未接收到数据认定链路断开(心跳包client <-> server,一应一答)

        static ByteBuffer m_bufferRecv = new ByteBuffer(1024);
        static CProtocolHandle m_handle = new CProtocolHandle();
        static CProtocolAnalysis m_analysis = new CProtocolAnalysis();

        public static void Start(string ip, int port)
        {
            m_ServerIP = ip;
            m_ServerPort = port;
            ThreadPool.QueueUserWorkItem(new WaitCallback(HeartThread));
        }

        public static void HeartThread(object sender)
        {
            int interval = 1000 * 30;

            while (true)
            {
                try
                {
                    if (!m_bConnection) ConnectServer();// 自动重连
                    if (m_bConnection) Send(GetProtocol("1001", 1, ""));// 心跳包发送
                }
                catch (Exception ex) 
                { 
                    LogHelper.AppendLog("[Error] [CSocketDAO]<HeartThread>:" + ex.Message); 
                }

                Thread.Sleep(interval);
            }
        }


        public static bool ConnectServer()
        {
            m_bConnection = false;

            try
            {
                IPEndPoint ipe = new IPEndPoint(IPAddress.Parse(m_ServerIP), m_ServerPort);
                m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                m_socket.Connect(ipe);

                m_bConnection = true;
                m_bExit = false;
                if (m_bFirst)
                {
                    m_bFirst = false;
                    WriteLog("后台通讯连接成功", false);
                }
                if (m_bHasStop)
                {
                    WriteLog("后台通讯连接成功", false);
                }
                lastRecvTime = DataHelper.GetNowTimestamp();
                m_bufferRecv.Clear();
                ThreadPool.QueueUserWorkItem(new WaitCallback(ReadThread));
            }
            catch 
            {
                if (m_bFirst)
                {
                    m_bFirst = false;
                    m_bHasStop = true;
                    WriteLog("后台通讯连接失败", true);
                }
            }

            return m_bConnection;
        }

        public static void StopConnection(string err)
        {
            if (!m_bConnection) return;
            m_bConnection = false;
            m_bExit = true;// 结束线程
            m_bHasStop = true;

            try { m_socket.Shutdown(SocketShutdown.Both); } catch { }
            try { m_socket.Shutdown(SocketShutdown.Both); } catch { }
            try { m_socket.Close(); } catch { }

            m_socket = null;

            WriteLog("后台通讯连接失败:" + err, true);
        }

        static void WriteLog(string strErr, bool bErr)
        {
            DataMgr.MainHandle.OnMessage("TCP信息：" + (bErr ? "[Error] " : "[Info] ") + strErr); 
        }
         
        public static void ReadThread(object sender)
        {
            byte[] readBuffer = new byte[1024];

            while (!m_bExit)
            {
                if (!m_bConnection) return;

                try
                {
                    int iSize = m_socket.Receive(readBuffer);
                    if (iSize > 0)
                    {
                        lastRecvTime = DataHelper.GetNowTimestamp();
                        m_bufferRecv.Put(readBuffer, 0, iSize);
                    }
                    else
                    {
                        StopConnection("recv: size == 0");// 侦测用
                        break;
                    }

                    m_analysis.BagStatus = CProtocolAnalysis.EBagStatus.BagNone;

                    // 粘包处理
                    while (m_bufferRecv.HasRemaining())
                    {
                        // 掉包处理
                        if (CProtocolAnalysis.EBagStatus.BagLost == m_analysis.BagStatus) break;

                        m_handle.Process(m_bufferRecv, m_analysis);// 数据解析(垃圾包处理) 

                        if (m_analysis.WhetherToSend)
                        {
                            Send(GetProtocol(m_analysis));
                        }
                    }
                }
                catch (Exception ex)
                {
                    StopConnection("recv: " + ex.Message);
                    break;
                } 
            }
        } 

        public static bool Send(byte[] data)
        {
            if (!m_bConnection) return false;

            if ((DataHelper.GetNowTimestamp() - lastRecvTime) >= outTime)
            {
                StopConnection("recv: Over Time");
                return false;
            }

            DataMgr.MainHandle.OnMessage("TCP信息：心跳发送");

            try
            {
                m_socket.Send(data);
            }
            catch (Exception ex)
            {
                StopConnection("send:" + ex.Message);// 由发送失败进行链路判断
                return false;
            }

            return true;
        }

        public static byte[] GetProtocol(string uid, int cmd, string msg)
        {
            string content = CProtocolHandle.SPLIT1 + uid + CProtocolHandle.SPLIT1 +
                            DataHelper.GetNowTimestamp() + CProtocolHandle.SPLIT1 +
                            DataHelper.Lpad(cmd, 2) + CProtocolHandle.SPLIT1 +
                            msg + CProtocolHandle.SPLIT1;
            StringBuilder data = new StringBuilder();
            data.Append("IOT=");
            data.Append(DataHelper.Lpad(DataHelper.GetLength(content), 4));
            data.Append(content);
            return DataHelper.StringToByte(data.ToString());
        }

        public static byte[] GetProtocol(CProtocolAnalysis analysis)
        {
            string content = CProtocolHandle.SPLIT1 + analysis.Uid + CProtocolHandle.SPLIT1 +
                            analysis.TaskId + CProtocolHandle.SPLIT1 +
                            DataHelper.Lpad(analysis.Cmd, 2) + CProtocolHandle.SPLIT1 +
                            analysis.Msg + CProtocolHandle.SPLIT1;
            StringBuilder data = new StringBuilder();
            data.Append("IOT=");
            data.Append(DataHelper.Lpad(DataHelper.GetLength(content), 4));
            data.Append(content);
            return DataHelper.StringToByte(data.ToString());
        }
    }
}